/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "MeshComponent.h"

#include "GameObject.h"
#include "SceneObject.h"
#include "MeshObject.h"
#include "PQException.h"

namespace PQEngine
{
	MeshComponent* MeshComponent::get(int id)
	{
		Object* obj=Object::getObject(id);
		if(obj==NULL){
			return NULL;
		}
		ObjectType objectType=obj->getObjectType();
		if(objectType==OBJECT_TYPE_COMPONENT_MESH){
			return (MeshComponent*)obj;
		}else{
			return NULL;
		}
	}


	MeshComponent::MeshComponent(MeshObject* object)
		: Component(OBJECT_TYPE_COMPONENT_MESH),_object(object),
		_entity(0),_highLighted(false),_animEnabled(false),_animIndex(0)
	{
		_entity=object->getEntity();
		initAnimations();
		initSubEntities();
	}

	void MeshComponent::reset()
	{
		_animationNames.clear();
		_animationStates.clear();

		_animIndex=0;
		_animEnabled=false;
		
		for(int i=0;i<MESH_MATERIAL_COUNT;i++){
			_materials[i].clear();
		}

		initAnimations();
		initSubEntities();
	}

	int MeshComponent::getNumSubMesh()
	{
		if(_entity){
			Ogre::MeshPtr meshPtr=_entity->getMesh();
			
			return meshPtr->getNumSubMeshes();
		}
		return 0;
		
	}

	std::string MeshComponent::getSubMeshMaterialName(unsigned short index)
	{
		if(_entity){
			Ogre::MeshPtr meshPtr=_entity->getMesh();
			Ogre::SubMesh *submesh=meshPtr->getSubMesh(index);
			return submesh->getMaterialName();
		}

		return "";	
	}

	MeshComponent::~MeshComponent()
	{
		if(_entity){
			SceneObject::getActive()->getSceneManager()->destroyEntity(_entity);
		}
	}
	
	void MeshComponent::enableAnim(bool state)
	{
		int size=_animationStates.size();
		for (int i = 0; i <size ; i++){
			_animationStates[i]->setEnabled(false);
			
		}

		_animEnabled=state;
		if (_animIndex>=0&&_animIndex<_animationStates.size()){
			_animationStates[_animIndex]->setEnabled(state);
		}
		
	}


	void MeshComponent::initAnimations()
	{
		if(_entity==NULL){
			return;
		}

		Ogre::AnimationStateSet *anims=_entity->getAllAnimationStates();
		if(!anims){
			return;
		}

		Ogre::AnimationStateIterator iter=anims->getAnimationStateIterator();
		while(iter.hasMoreElements()){
			Ogre::AnimationStateMap::mapped_type i=iter.getNext();
			_animationNames.push_back(i->getAnimationName());
		}

		Ogre::SkeletonInstance* skeleton = _entity->getSkeleton();
		if(skeleton){
			skeleton->setBlendMode(Ogre::ANIMBLEND_CUMULATIVE);
		}

		int size=_animationNames.size();
		_animationStates.resize(size);
		for (int i = 0; i <size ; i++){
			_animationStates[i] = _entity->getAnimationState(_animationNames[i]);
			_animationStates[i]->setLoop(true);
		}
	}

	

	//manager mesh animations and...
	void MeshComponent::update(float deltaT)
	{
		if(_animEnabled){
			if (_animIndex>=0&&_animIndex<_animationStates.size()){
				_animationStates[_animIndex]->addTime(deltaT);
			}
		}
	}

	void MeshComponent::setMaterialName(std::string materialName)
	{
		if(_entity){
			_entity->setMaterialName(materialName);
		}
	}

	float MeshComponent::getRadius()
	{
		if(_entity){
			float radius=_entity->getBoundingRadius();	
			return radius;
		}else{
			return 0;
		}
	}

	Ogre::Vector3 MeshComponent::getBoundingBoxSize(void) const
	{
		if(_entity){
			Ogre::AxisAlignedBox box=_entity->getBoundingBox();
			Ogre::Vector3 size=box.getSize();
			return size;
		}else{
			return Ogre::Vector3(0,0,0);
		}
	}

	Ogre::AxisAlignedBox MeshComponent::getBoundingBox(void) const
	{
		if(_entity){			
			Ogre::AxisAlignedBox box=_entity->getBoundingBox();			
			return box;
		}else{
			return Ogre::AxisAlignedBox::BOX_NULL;
		}
	}


	Ogre::AxisAlignedBox MeshComponent::getWorldBoundingBox(void) const
	{
		if(_entity){
			Ogre::AxisAlignedBox box=_entity->getWorldBoundingBox();
			return box;
		}else{
			return Ogre::AxisAlignedBox::BOX_NULL;
		}
	}

	Ogre::MaterialPtr MeshComponent::getMaterial(Ogre::String name)
	{
		Ogre::MaterialPtr ptr;
		
		int cnt=_entity->getNumSubEntities();
		for(int i=0;i<cnt;i++){
			Ogre::SubEntity *entity=_entity->getSubEntity(i);
			//if(entity->getMaterialName()==name){
			//}
			ptr=entity->getMaterial();
			if(ptr->getName()==name){
				return ptr;
			}
			
		}


		return ptr;
	}


	void MeshComponent::initSubEntities()
	{
		assert(_entity);
		int cnt=_entity->getNumSubEntities();

		//Ogre::MaterialManager::getSingletonPtr()->getByName();
		//init normal material
		for(int i=0;i<cnt;i++){
			Ogre::SubEntity *entity=_entity->getSubEntity(i);
			Ogre::MaterialPtr mat=entity->getMaterial();
			
			_materials[MESH_MATERIAL_NORMAL].push_back(mat);
		}

		//create highlight material
		for(int i=0;i<cnt;i++){
			Ogre::String name=_materials[MESH_MATERIAL_NORMAL][i]->getName()+"/HighLighted";
			Ogre::MaterialPtr mat= _materials[MESH_MATERIAL_NORMAL][i]->clone(name);
			int techcnt=mat->getNumTechniques();
			for(int j=0;j<techcnt;j++){
				Ogre::Technique* tech=mat->getTechnique(j);
				Ogre::Pass* pass=tech->createPass();
				
				pass->setPolygonMode(Ogre::PM_WIREFRAME);
				pass->setNormaliseNormals(true);
				pass->setSceneBlendingOperation(Ogre::SBO_MAX);
				pass->setSelfIllumination(Ogre::ColourValue(1.0f,1.0f,1.0f));
				
			}
			_materials[MESH_MATERIAL_HIGHLIGHT].push_back(mat);

		}


	}

	void MeshComponent::setNormalMaterial()
	{
		assert(_entity);
		int cnt=_entity->getNumSubEntities();
		for(int i=0;i<cnt;i++){
			Ogre::SubEntity *ent=_entity->getSubEntity(i);
			ent->setMaterial(_materials[MESH_MATERIAL_NORMAL][i]);
			
		}
	}

	void MeshComponent::highLight(bool lighted)
	{
		if(_highLighted==lighted){
			return;
		}

		int cnt=_entity->getNumSubEntities();
		for(int i=0;i<cnt;i++){
			if(lighted){
				Ogre::SubEntity *ent=_entity->getSubEntity(i);
				ent->setMaterial(_materials[MESH_MATERIAL_HIGHLIGHT][i]);
			}else{
				Ogre::SubEntity *ent=_entity->getSubEntity(i);
				ent->setMaterial(_materials[MESH_MATERIAL_NORMAL][i]);
			}
		}
		

		_highLighted=lighted;
	}


	/*Set current animation by index.
	*/
	void MeshComponent::setAnim(int index)
	{
		int cnt=_animationStates.size();
		PQAssert(index>=0&&index<cnt);
		_animIndex=index;
	}


	Ogre::String MeshComponent::getAnimName(int index)
	{
		int cnt=_animationNames.size();
		PQAssert(index>=0&&index<cnt);
		return _animationNames[index];
	}

	void MeshComponent::setAnim(Ogre::String name)
	{
		int cnt=_animationNames.size();
		for(int i=0;i<cnt;i++){
			if(_animationNames[i]==name){
				setAnim(i);
			}
		}
	}


}
